<?php
// +----------------------------------------------------------------------
// | Author: Zaker <49007623@qq.com>
// +----------------------------------------------------------------------
namespace esclass;
/**


* DEMO:

$tpl = new iSmarty();

$tpl->assign('Name','名字');



$data['title']='标题';

$data['url']='http://127.0.0.1';

$tpl->assign($data);

$tpl->display('template.htm');

*/
class ismarty{
	/*

	* 模板目录

	* @var string

	*/
	public $compiledir = './runtime/temp/';
	public $templatedir = './Template';
	// 引擎配置
	public $config = [
			'view_path'          => '', // 模板路径
			'view_base'          => '',
			'view_suffix'        => 'html', // 默认模板文件后缀
			'view_depr'          => DS,
			'cache_suffix'       => 'php', // 默认模板缓存后缀
			'tpl_deny_func_list' => 'echo,exit', // 模板引擎禁用函数
			'tpl_deny_php'       => false, // 默认模板引擎是否禁用PHP原生代码
			'tpl_begin'          => '{', // 模板引擎普通标签开始标记
			'tpl_end'            => '}', // 模板引擎普通标签结束标记
			'strip_space'        => false, // 是否去除模板文件里面的html空格与换行
			'tpl_cache'          => true, // 是否开启模板编译缓存,设为false则每次都会重新编译
			'compile_type'       => 'file', // 模板编译类型
			'cache_prefix'       => '', // 模板缓存前缀标识，可以动态改变
			'cache_time'         => 0, // 模板缓存有效期 0 为永久，(以数字为值，单位:秒)
			'layout_on'          => false, // 布局模板开关
			'layout_name'        => 'layout', // 布局模板入口文件
			'layout_item'        => '{__CONTENT__}', // 布局模板的内容替换标识
			'taglib_begin'       => '{', // 标签库标签开始标记
			'taglib_end'         => '}', // 标签库标签结束标记
			'taglib_load'        => true, // 是否使用内置标签库之外的其它标签库，默认自动检测
			'taglib_build_in'    => 'cx', // 内置标签库名称(标签使用不必指定标签库名称),以逗号分隔 注意解析顺序
			'taglib_pre_load'    => '', // 需要额外加载的标签库(须指定标签库名称)，多个以逗号分隔
			'display_cache'      => false, // 模板渲染缓存
			'cache_id'           => '', // 模板缓存ID
			'tpl_replace_string' => [],
			'tpl_var_identify'   => 'array', // .语法变量识别，array|object|'', 为空时自动识别
	];
	protected $data = [];
	/*

	* 模板变量

	* @var array

	*/
	protected $vars = array();
	private $includeFile = []; // 记录所有模板包含的文件路径及更新时间
	
	// 视图实例
	protected static $instance;
	/**
	 * 初始化

	 */
	public static function instance($options = [])
	{
		if (is_null(self::$instance)) {
			 
			self::$instance = new static($options);
			 
		}
		return self::$instance;
	}
	/**
	 * 构造函数
	 * @access public
	 */
	private function __construct(array $config = [])
	{
		
		$this->config['cache_path']   = TEMP_PATH;
		
		$this->config['taglib_begin'] = $this->stripPreg($this->config['taglib_begin']);
		$this->config['taglib_end']   = $this->stripPreg($this->config['taglib_end']);
		$this->config['tpl_begin']    = $this->stripPreg($this->config['tpl_begin']);
		$this->config['tpl_end']      = $this->stripPreg($this->config['tpl_end']);


	}
   public function setconfig($config){
	$this->config                 = array_merge($this->config, $config);
   }


	/**
	 * 字符串替换 避免正则混淆
	 * @access private
	 * @param string $str
	 * @return string
	 */
	private function stripPreg($str)
	{
		return str_replace(
				['{', '}', '(', ')', '|', '[', ']', '-', '+', '*', '.', '^', '?'],
				['\{', '\}', '\(', '\)', '\|', '\[', '\]', '\-', '\+', '\*', '\.', '\^', '\?'],
				$str);
	}
	
	/*

	* 编译PHP标签

	* @param string $tpl (template file)

	*/
	protected function parse($tplfile){
		// 加载模板文件 //
		
		
		//$tplfile=$this->parseTemplateFile($tpl);
		//$tplfile = $this->templatedir .$tpl;
		
		
		
		$fp   = @fopen($tplfile, 'r');
		$text = fread($fp, filesize($tplfile));
		fclose($fp);
		
		// 解析继承
		$this->parseExtend($text);
		
		// 检查include语法
		$this->parseInclude($text);
		
		// 检查PHP语法
		$this->parsePhp($text);
		
		// 获取需要引入的标签库列表
		// 标签库只需要定义一次，允许引入多个一次
		// 一般放在文件的最前面
		// 格式：<taglib name="html,mytag..." />
		// 当TAGLIB_LOAD配置为true时才会进行检测
		if ($this->config['taglib_load']) {
			$tagLibs = $this->getIncludeTagLib($text);
			if (!empty($tagLibs)) {
				// 对导入的TagLib进行解析
				foreach ($tagLibs as $tagLibName) {
					$this->parseTagLib($tagLibName, $text);
				}
			}
		}
		
		// 预先加载的标签库 无需在每个模板中使用taglib标签加载 但必须使用标签库XML前缀
		if ($this->config['taglib_pre_load']) {
			
			
			
			$tagLibs = explode(',', $this->config['taglib_pre_load']);
			foreach ($tagLibs as $tag) {
				
				$this->parseTagLib($tag, $text);
			}
		}
		
		
		// 内置标签库 无需使用taglib标签导入就可以使用 并且不需使用标签库XML前缀
		
		$tagLibs = explode(',', $this->config['taglib_build_in']);
		
		foreach ($tagLibs as $tag) {
			$this->parseTagLib($tag, $text, true);
		}
		
		// 解析普通模板标签 {$tagName}
		$this->parseTag($text);
		
	
		
		// 创建编译文件 //
		$compliefile =TEMP_PATH . md5($tplfile) . '.php';
		
		if(!is_dir(TEMP_PATH)){
			$res=mkdir(iconv("UTF-8", "GBK", TEMP_PATH),0777,true);
			if ($res){
				
			}else{
			
			}
		}
		
	
		
	if ($fp = @fopen($compliefile, 'w')) {
			
			fputs($fp, $text);
			
			fclose($fp);
		}
	}
	/**
	 * 模板变量获取
	 * @access public
	 * @param  string $name 变量名
	 * @return mixed
	 */
	public function get($name = '')
	{
		
		if ('' == $name) {
			return $this->data;
		} else {
			$data = $this->data;
			foreach (explode('.', $name) as $key => $val) {
				if (isset($data[$val])) {
					$data = $data[$val];
				} else {
					$data = null;
					break;
				}
			}
			return $data;
		}
	}
	private function parseCL($content){
	
	
		
	$cl =$this->config['view_replace_str'];
	
	foreach ($cl as $key =>$v){
		
		$content =str_replace($key, $v, $content);
	
		
	}
	
	return $content;
	
}
	/**
	 * 模板标签解析
	 * 格式： {TagName:args [|content] }
	 * @access private
	 * @param  string $content 要解析的模板内容
	 * @return void
	 */
	private function parseTag(&$content)
	{
		$regex = $this->getRegex('tag');
		if (preg_match_all($regex, $content, $matches, PREG_SET_ORDER)) {
			foreach ($matches as $match) {
				$str  = stripslashes($match[1]);
				$flag = substr($str, 0, 1);
				switch ($flag) {
					case '$':
						// 解析模板变量 格式 {$varName}
						// 是否带有?号
						if (false !== $pos = strpos($str, '?')) {
							$array = preg_split('/([!=]={1,2}|(?<!-)[><]={0,1})/', substr($str, 0, $pos), 2, PREG_SPLIT_DELIM_CAPTURE);
							$name  = $array[0];
							$this->parseVar($name);
							
							$this->parseVarFunction($name);
	
							$str = trim(substr($str, $pos + 1));
							$this->parseVar($str);
							$first = substr($str, 0, 1);
							if (strpos($name, ')')) {
								// $name为对象或是自动识别，或者含有函数
								if (isset($array[1])) {
									$this->parseVar($array[2]);
									$name .= $array[1] . $array[2];
								}
								switch ($first) {
									case '?':
										$str = '<?php echo (' . $name . ') ? ' . $name . ' : ' . substr($str, 1) . '; ?>';
										break;
									case '=':
										$str = '<?php if(' . $name . ') echo ' . substr($str, 1) . '; ?>';
										break;
									default:
										$str = '<?php echo ' . $name . '?' . $str . '; ?>';
								}
							} else {
								if (isset($array[1])) {
									$this->parseVar($array[2]);
									$_name = ' && ' . $name . $array[1] . $array[2];
								} else {
									$_name = '';
								}
								// $name为数组
								switch ($first) {
									case '?':
										// {$varname??'xxx'} $varname有定义则输出$varname,否则输出xxx
										$str = '<?php echo isset(' . $name . ')' . $_name . ' ? ' . $name . ' : ' . substr($str, 1) . '; ?>';
										break;
									case '=':
										// {$varname?='xxx'} $varname为真时才输出xxx
										$str = '<?php if(!empty(' . $name . ')' . $_name . ') echo ' . substr($str, 1) . '; ?>';
										break;
									case ':':
										// {$varname?:'xxx'} $varname为真时输出$varname,否则输出xxx
										$str = '<?php echo !empty(' . $name . ')' . $_name . '?' . $name . $str . '; ?>';
										break;
									default:
										if (strpos($str, ':')) {
											// {$varname ? 'a' : 'b'} $varname为真时输出a,否则输出b
											$str = '<?php echo !empty(' . $name . ')' . $_name . '?' . $str . '; ?>';
										} else {
											$str = '<?php echo ' . $_name . '?' . $str . '; ?>';
										}
								}
							}
						} else {
							$this->parseVar($str);
							$this->parseVarFunction($str);
							$str = '<?php echo ' . $str . '; ?>';
						}
						break;
					case ':':
						// 输出某个函数的结果
						$str = substr($str, 1);
						$this->parseVar($str);
						$str = '<?php echo ' . $str . '; ?>';
						break;
					case '~':
						// 执行某个函数
						$str = substr($str, 1);
						$this->parseVar($str);
						$str = '<?php ' . $str . '; ?>';
						break;
					case '-':
					case '+':
						// 输出计算
						$this->parseVar($str);
						$str = '<?php echo ' . $str . '; ?>';
						break;
					case '/':
						// 注释标签
						$flag2 = substr($str, 1, 1);
						if ('/' == $flag2 || ('*' == $flag2 && substr(rtrim($str), -2) == '*/')) {
							$str = '';
						}
						break;
					default:
						// 未识别的标签直接返回
						$str = $this->config['tpl_begin'] . $str . $this->config['tpl_end'];
						break;
				}
				$content = str_replace($match[0], $str, $content);
			}
			unset($matches);
		}
		return;
	}
	/**
	 * 搜索模板页面中包含的TagLib库
	 * 并返回列表
	 * @access private
	 * @param  string $content 模板内容
	 * @return array|null
	 */
	private function getIncludeTagLib(&$content)
	{
		
		// 搜索是否有TagLib标签
		if (preg_match($this->getRegex('taglib'), $content, $matches)) {
			// 替换TagLib标签
			$content = str_replace($matches[0], '', $content);
			return explode(',', $matches['name']);
		}
		
		return;
	}
	
	/**
	 * TagLib库解析
	 * @access public
	 * @param  string   $tagLib 要解析的标签库
	 * @param  string   $content 要解析的模板内容
	 * @param  boolean  $hide 是否隐藏标签库前缀
	 * @return void
	 */
	public function parseTagLib($tagLib, &$content, $hide = false)
	{
		
		if (false !== strpos($tagLib, '\\')) {
			// 支持指定标签库的命名空间
			$className = $tagLib;
			$tagLib    = substr($tagLib, strrpos($tagLib, '\\') + 1);
		} else {
			
			$className = '\\esclass\\taglib\\' . ucwords($tagLib);
			
		}
		
		
		
		$tLib = new $className($this);
		
		
		
		$tLib->parseTag($content, $hide ? '' : $tagLib);
		
		return;
	}
	/**
	 * 对模板中使用了函数的变量进行解析
	 * 格式 {$varname|function1|function2=arg1,arg2}
	 * @access public
	 * @param  string $varStr 变量字符串
	 * @return void
	 */
	public function parseVarFunction(&$varStr)
	{
		if (false == strpos($varStr, '|')) {
			return;
		}
		static $_varFunctionList = [];
		$_key                    = md5($varStr);
		//如果已经解析过该变量字串，则直接返回变量值
		if (isset($_varFunctionList[$_key])) {
			$varStr = $_varFunctionList[$_key];
		} else {
			$varArray = explode('|', $varStr);
			// 取得变量名称
			$name = array_shift($varArray);
			// 对变量使用函数
			$length = count($varArray);
			// 取得模板禁止使用函数列表
			$template_deny_funs = explode(',', $this->config['tpl_deny_func_list']);
			for ($i = 0; $i < $length; $i++) {
				$args = explode('=', $varArray[$i], 2);
				// 模板函数过滤
				$fun = trim($args[0]);
				switch ($fun) {
					case 'default': // 特殊模板函数
						if (false === strpos($name, '(')) {
							$name = '(isset(' . $name . ') && (' . $name . ' !== \'\')?' . $name . ':' . $args[1] . ')';
						} else {
							$name = '(' . $name . ' ?: ' . $args[1] . ')';
						}
						break;
					default: // 通用模板函数
						if (!in_array($fun, $template_deny_funs)) {
							if (isset($args[1])) {
								if (strstr($args[1], '###')) {
									$args[1] = str_replace('###', $name, $args[1]);
									$name    = "$fun($args[1])";
								} else {
									$name = "$fun($name,$args[1])";
								}
							} else {
								if (!empty($args[0])) {
									$name = "$fun($name)";
								}
							}
						}
				}
			}
			$_varFunctionList[$_key] = $name;
			$varStr                  = $name;
		}
		return;
	}
	/**
	 * 模板变量解析,支持使用函数
	 * 格式： {$varname|function1|function2=arg1,arg2}
	 * @access public
	 * @param  string $varStr 变量数据
	 * @return void
	 */
	public function parseVar(&$varStr)
	{
		$varStr = trim($varStr);
		
		if (preg_match_all('/\$[a-zA-Z_](?>\w*)(?:[:\.][0-9a-zA-Z_](?>\w*))+/', $varStr, $matches, PREG_OFFSET_CAPTURE)) {
			
			static $_varParseList = [];
			while ($matches[0]) {
				$match = array_pop($matches[0]);
				//如果已经解析过该变量字串，则直接返回变量值
				if (isset($_varParseList[$match[0]])) {
					$parseStr = $_varParseList[$match[0]];
				} else {
					if (strpos($match[0], '.')) {
						$vars  = explode('.', $match[0]);
						
						$first = array_shift($vars);
						if ('$Think' == $first) {
							// 所有以Think.打头的以特殊变量对待 无需模板赋值就可以输出
							$parseStr = $this->parseThinkVar($vars);
							
						} elseif ('$Request' == $first) {
							// 获取Request请求对象参数
							$method = array_shift($vars);
							if (!empty($vars)) {
								$params = implode('.', $vars);
								if ('true' != $params) {
									$params = '\'' . $params . '\'';
								}
							} else {
								$params = '';
							}
							$parseStr = '\esclass\Request::instance()->' . $method . '(' . $params . ')';
						} else {
							switch ($this->config['tpl_var_identify']) {
								case 'array': // 识别为数组
									$parseStr = $first . '[\'' . implode('\'][\'', $vars) . '\']';
									break;
								case 'obj': // 识别为对象
									$parseStr = $first . '->' . implode('->', $vars);
									break;
								default: // 自动判断数组或对象
									$parseStr = '(is_array(' . $first . ')?' . $first . '[\'' . implode('\'][\'', $vars) . '\']:' . $first . '->' . implode('->', $vars) . ')';
							}
						}
					} else {
						$parseStr = str_replace(':', '->', $match[0]);
					}
					$_varParseList[$match[0]] = $parseStr;
				}
				$varStr = substr_replace($varStr, $parseStr, $match[1], strlen($match[0]));
			}
			unset($matches);
		}
		return;
	}
	/**
	 * 特殊模板变量解析
	 * 格式 以 $Think. 打头的变量属于特殊模板变量
	 * @access public
	 * @param  array $vars 变量数组
	 * @return string
	 */
	public function parseThinkVar($vars)
	{
		$type  = strtoupper(trim(array_shift($vars)));
		$param = implode('.', $vars);
		if ($vars) {
			switch ($type) {
				case 'SERVER':
					$parseStr = '\\esclass\\Request::instance()->server(\'' . $param . '\')';
					break;
				case 'GET':
					$parseStr = '\\esclass\\Request::instance()->get(\'' . $param . '\')';
					break;
				case 'POST':
					$parseStr = '\\esclass\\Request::instance()->post(\'' . $param . '\')';
					break;
				case 'COOKIE':
					$parseStr = '$_COOKIE(\'' . $param . '\')';
					break;
				case 'SESSION':
					$parseStr = '$_SESSION(\'' . $param . '\')';
					break;
				case 'ENV':
					$parseStr = '\\esclass\\Request::instance()->env(\'' . $param . '\')';
					break;
				case 'REQUEST':
					$parseStr = '\\esclass\\Request::instance()->request(\'' . $param . '\')';
					break;
				case 'CONST':
					$parseStr = strtoupper($param);
					break;

				case 'CONFIG':
					$parseStr ='webconfig(\''.$param.'\')';
					break;
				default:
					$parseStr = '\'\'';
					break;
			}
		} else {
			switch ($type) {
				case 'NOW':
					$parseStr = "date('Y-m-d g:i a',time())";
					break;
				case 'VERSION':
					$parseStr = 'ESPHP_VERSION';
					break;
				case 'LDELIM':
					$parseStr = '\'' . ltrim($this->config['tpl_begin'], '\\') . '\'';
					break;
				case 'RDELIM':
					$parseStr = '\'' . ltrim($this->config['tpl_end'], '\\') . '\'';
					break;
				default:
					if (defined($type)) {
						$parseStr = $type;
					} else {
						$parseStr = '';
					}
			}
		}
		return $parseStr;
	}
	/**
	 * 分析标签属性
	 * @access public
	 * @param  string   $str 属性字符串
	 * @param  string   $name 不为空时返回指定的属性名
	 * @return array
	 */
	public function parseAttr($str, $name = null)
	{
		$regex = '/\s+(?>(?P<name>[\w-]+)\s*)=(?>\s*)([\"\'])(?P<value>(?:(?!\\2).)*)\\2/is';
		$array = [];
		if (preg_match_all($regex, $str, $matches, PREG_SET_ORDER)) {
			foreach ($matches as $match) {
				$array[$match['name']] = $match['value'];
			}
			unset($matches);
		}
		if (!empty($name) && isset($array[$name])) {
			return $array[$name];
		} else {
			return $array;
		}
	}
	/**
	 * 解析模板中的include标签
	 * @access private
	 * @param  string $content 要解析的模板内容
	 * @return void
	 */
	private function parseInclude(&$content)
	{
		$regex = $this->getRegex('include');
		$func  = function ($template) use (&$func, &$regex, &$content) {
			
			if (preg_match_all($regex, $template, $matches, PREG_SET_ORDER)) {
				foreach ($matches as $match) {
					$array = $this->parseAttr($match[0]);
					$file  = $array['file'];
					unset($array['file']);
					// 分析模板文件名并读取内容
					$parseStr = $this->parseTemplateName($file);
					foreach ($array as $k => $v) {
						// 以$开头字符串转换成模板变量
						if (0 === strpos($v, '$')) {
							$v = $this->get(substr($v, 1));
						}
						$parseStr = str_replace('[' . $k . ']', $v, $parseStr);
					}
					$content = str_replace($match[0], $parseStr, $content);
					// 再次对包含文件进行模板分析
					$func($parseStr);
				}
				unset($matches);
			}
		};
		// 替换模板中的include标签
		$func($content);
		return;
	}
	/**
	 * 解析模板文件名
	 * @access private
	 * @param  string $template 文件名
	 * @return string|false
	 */
	public function parseTemplateFile($template)
	{
		//dump($template);
		if ('' == pathinfo($template, PATHINFO_EXTENSION)) {
			
			
			
			if (strpos($template, '@')) {
				list($ml,$module, $classname,$template) = explode('@', $template);
			}
		
			$module = isset($module) ? $module : Request::instance()->module();
			
			$config = load_config(APP_PATH.DS.$module.'/config.php');
			
			//$this->config = array_merge($config,$this->config);
			
			
			if(isset($ml)){
					
				$hometheme=ROOT_PATH.$ml.DS.$classname.'/view/';
					
			}else{
				if($module=='index'){
						
					$config['tpl'] =webconfig('site_tpl');
				
				}
					
				if(empty($config['tpl'])){
				//只要不是定义的tpl，都走默认的路径
						
					$hometheme=APP_PATH.$module.'/view/';
				
					$theme_path='';
				
				}else{
						
					$hometheme='./template/'.$config['tpl'].'/';
					$theme_path = 'PC'; //设置主题默认路径为电脑端
					if(webconfig('OPEN_WAP')==1){
						//开启wap时
					
						if (Request::instance()->isMobile()){
							$theme_path = 'WAP'; //设置主题默认路径为电脑端
					
						}
							
							
							
					}
				}
				

					
				$hometheme=$hometheme.$theme_path.'/';
			}
			
			

			

				
			$config['view_base']=$hometheme;
			
			if($this->config['view_base']!=$config['view_base']){
				$this->setconfig($config);
			}
			
			if(!isset($ml)){
				if (0 !== strpos($template, '/')) {
				
				
					$template = str_replace(['/', ':'], $this->config['view_depr'], $template);
				
						
				
				} else {
				
					$template = str_replace(['/', ':'], $this->config['view_depr'], substr($template, 1));
				
				}
			}

			
		    $path   = $this->config['view_base'];
		    
		    
		    
			$template = $path . $template . '.' . ltrim($this->config['view_suffix'], '.');
			
			
		}else{
			
			$module = isset($module) ? $module : Request::instance()->module();
				
			$config = load_config(APP_PATH.DS.$module.'/config.php');
			
			$this->setconfig($config);
			
			
		}

		if (is_file($template)) {
			// 记录模板文件的更新时间
			$this->includeFile[$template] = filemtime($template);
			
			return $template;
		} else {
			throw new EsException('模板不存在'.$template);
		}
	}
	/**
	 * 分析加载的模板文件并读取内容 支持多个模板文件读取
	 * @access private
	 * @param  string $templateName 模板文件名
	 * @return string
	 */
	private function parseTemplateName($templateName)
	{
		$array    = explode(',', $templateName);
		
		
		
		$parseStr = '';
		foreach ($array as $templateName) {
			if (empty($templateName)) {
				continue;
			}
			
			if (0 === strpos($templateName, '$')) {
				//支持加载变量文件名
				
				$templateName = $this->get(substr($templateName, 1));
				
			}
			
			$template = $this->parseTemplateFile($templateName);
			
			if ($template) {
				// 获取模板文件内容
				$parseStr .= file_get_contents($template);
				
			}
		}
		return $parseStr;
	}
	/**
	 * 检查PHP语法
	 * @access private
	 * @param string $content 要解析的模板内容
	 * @return void
	 * @throws \think\Exception
	 */
	private function parsePhp(&$content)
	{
		// 短标签的情况要将<?标签用echo方式输出 否则无法正常输出xml标识
		$content = preg_replace('/(<\?(?!php|=|$))/i', '<?php echo \'\\1\'; ?>' . "\n", $content);
		// PHP语法检查
		if ($this->config['tpl_deny_php'] && false !== strpos($content, '<?php')) {
			throw new Exception('not allow php tag', 11600);
		}
		return;
	}
	/**
	 * 解析模板中的extend标签
	 * @access private
	 * @param  string $content 要解析的模板内容
	 * @return void
	 */
	private function parseExtend(&$content)
	{
		
		$regex  = self::getRegex('extend');
		$array  = $blocks  = $baseBlocks  = [];
		$extend = '';
		
		$func   = function ($template) use (&$func, &$regex, &$array, &$extend, &$blocks, &$baseBlocks) {
			
			
			
			if (preg_match($regex, $template, $matches)) {
				
				if (!isset($array[$matches['name']])) {
					$array[$matches['name']] = 1;
					
					
					
					// 读取继承模板
					$extend = $this->parseTemplateName($matches['name']);
					
					// 递归检查继承
					$func($extend);
					// 取得block标签内容
					$blocks = array_merge($blocks, $this->parseBlock($template));
					
					
					
					return;
				}
			} else {
				// 取得顶层模板block标签内容
				$baseBlocks = self::parseBlock($template, true);
				if (empty($extend)) {
					// 无extend标签但有block标签的情况
					$extend = $template;
				}
			}
		};
		
		$func($content);
		if (!empty($extend)) {
			
			if ($baseBlocks) {
				$children = [];
				foreach ($baseBlocks as $name => $val) {
					$replace = $val['content'];
					
					if (!empty($children[$name])) {
						// 如果包含有子block标签
						foreach ($children[$name] as $key) {
							$replace = str_replace($baseBlocks[$key]['begin'] . $baseBlocks[$key]['content'] . $baseBlocks[$key]['end'], $blocks[$key]['content'], $replace);
						}
					}
					if (isset($blocks[$name])) {
						// 带有{__block__}表示与所继承模板的相应标签合并，而不是覆盖
						$replace = str_replace(['{__BLOCK__}', '{__block__}'], $replace, $blocks[$name]['content']);
						if (!empty($val['parent'])) {
							// 如果不是最顶层的block标签
							$parent = $val['parent'];
							if (isset($blocks[$parent])) {
								$blocks[$parent]['content'] = str_replace($blocks[$name]['begin'] . $blocks[$name]['content'] . $blocks[$name]['end'], $replace, $blocks[$parent]['content']);
							}
							$blocks[$name]['content'] = $replace;
							$children[$parent][]      = $name;
							continue;
						}
					} elseif (!empty($val['parent'])) {
						// 如果子标签没有被继承则用原值
						$children[$val['parent']][] = $name;
						$blocks[$name]              = $val;
					}
					if (!$val['parent']) {
						// 替换模板中的顶级block标签
						$extend = str_replace($val['begin'] . $val['content'] . $val['end'], $replace, $extend);
					}
					
				}
			}
			$content = $extend;
			unset($blocks, $baseBlocks);
		}
		return;
	}
	/**
	 * 获取模板中的block标签
	 * @access private
	 * @param  string   $content 模板内容
	 * @param  boolean  $sort 是否排序
	 * @return array
	 */
	private function parseBlock(&$content, $sort = false)
	{
		$regex  = $this->getRegex('block');
		$result = [];
		if (preg_match_all($regex, $content, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) {
			$right = $keys = [];
			foreach ($matches as $match) {
				if (empty($match['name'][0])) {
					if (count($right) > 0) {
						$tag                  = array_pop($right);
						$start                = $tag['offset'] + strlen($tag['tag']);
						$length               = $match[0][1] - $start;
						$result[$tag['name']] = [
								'begin'   => $tag['tag'],
								'content' => substr($content, $start, $length),
								'end'     => $match[0][0],
								'parent'  => count($right) ? end($right)['name'] : '',
						];
						$keys[$tag['name']] = $match[0][1];
					}
				} else {
					// 标签头压入栈
					$right[] = [
							'name'   => $match[2][0],
							'offset' => $match[0][1],
							'tag'    => $match[0][0],
					];
				}
			}
			unset($right, $matches);
			if ($sort) {
				// 按block标签结束符在模板中的位置排序
				array_multisort($keys, $result);
			}
		}
		return $result;
	}
	
	 private function getRegex($tagName)
    {
        $regex = '';
        if ('tag' == $tagName) {
            $begin = $this->config['tpl_begin'];
            $end   = $this->config['tpl_end'];
            if (strlen(ltrim($begin, '\\')) == 1 && strlen(ltrim($end, '\\')) == 1) {
                $regex = $begin . '((?:[\$]{1,2}[a-wA-w_]|[\:\~][\$a-wA-w_]|[+]{2}[\$][a-wA-w_]|[-]{2}[\$][a-wA-w_]|\/[\*\/])(?>[^' . $end . ']*))' . $end;
            } else {
                $regex = $begin . '((?:[\$]{1,2}[a-wA-w_]|[\:\~][\$a-wA-w_]|[+]{2}[\$][a-wA-w_]|[-]{2}[\$][a-wA-w_]|\/[\*\/])(?>(?:(?!' . $end . ').)*))' . $end;
            }
        } else {
            $begin  = $this->config['taglib_begin'];
            $end    = $this->config['taglib_end'];
            $single = strlen(ltrim($begin, '\\')) == 1 && strlen(ltrim($end, '\\')) == 1 ? true : false;
            switch ($tagName) {
                case 'block':
                    if ($single) {
                        $regex = $begin . '(?:' . $tagName . '\b(?>(?:(?!name=).)*)\bname=([\'\"])(?P<name>[\$\w\-\/\.]+)\\1(?>[^' . $end . ']*)|\/' . $tagName . ')' . $end;
                    } else {
                        $regex = $begin . '(?:' . $tagName . '\b(?>(?:(?!name=).)*)\bname=([\'\"])(?P<name>[\$\w\-\/\.]+)\\1(?>(?:(?!' . $end . ').)*)|\/' . $tagName . ')' . $end;
                    }
                    break;
                case 'literal':
                    if ($single) {
                        $regex = '(' . $begin . $tagName . '\b(?>[^' . $end . ']*)' . $end . ')';
                        $regex .= '(?:(?>[^' . $begin . ']*)(?>(?!' . $begin . '(?>' . $tagName . '\b[^' . $end . ']*|\/' . $tagName . ')' . $end . ')' . $begin . '[^' . $begin . ']*)*)';
                        $regex .= '(' . $begin . '\/' . $tagName . $end . ')';
                    } else {
                        $regex = '(' . $begin . $tagName . '\b(?>(?:(?!' . $end . ').)*)' . $end . ')';
                        $regex .= '(?:(?>(?:(?!' . $begin . ').)*)(?>(?!' . $begin . '(?>' . $tagName . '\b(?>(?:(?!' . $end . ').)*)|\/' . $tagName . ')' . $end . ')' . $begin . '(?>(?:(?!' . $begin . ').)*))*)';
                        $regex .= '(' . $begin . '\/' . $tagName . $end . ')';
                    }
                    break;
                case 'restoreliteral':
                    $regex = '<!--###literal(\d+)###-->';
                    break;
                case 'include':
                    $name = 'file';
                case 'taglib':
                case 'layout':
                case 'extend':
                    if (empty($name)) {
                        $name = 'name';
                    }
                    if ($single) {
                        $regex = $begin . $tagName . '\b(?>(?:(?!' . $name . '=).)*)\b' . $name . '=([\'\"])(?P<name>[\$\w\-\/\.\:@,\\\\]+)\\1(?>[^' . $end . ']*)' . $end;
                    } else {
                        $regex = $begin . $tagName . '\b(?>(?:(?!' . $name . '=).)*)\b' . $name . '=([\'\"])(?P<name>[\$\w\-\/\.\:@,\\\\]+)\\1(?>(?:(?!' . $end . ').)*)' . $end;
                    }
                    break;
            }
        }
        return '/' . $regex . '/is';
    }
	/*

	* 模板变量值赋

	* @param array|string $k the template variable name(s)

	* @param mixed $v the value to assign

	*/
	public function assign($k, $v = null)
	{
/* 		if(is_array($k) && $v==null){
			foreach($k as $key=>$value){
				$this->vars[$key] = $value;
			}
		}else{
			$this->vars[$k] = $v;
		} */
		
		if (is_array($k)) {
			$this->data = array_merge($this->data, $k);
		} else {
			$this->data[$k] = $v;
		}
		return $this;
	}
	 
	/*

	* 设置模板目录

	* @param string $str (path)

	*/
	protected function templateDir($path)
	{
		$this->templatedir = $this->pathCheck($path);
	}
	 

	 
	/*

	* 检查路径的最后一个字符

	* @param string $str (path)

	* @return string

	*/
	protected function pathCheck($str){
		
		return (preg_match('/\/$/', $str)) ? $str : $str . '/';
	}
	 
	/*

	* 执行与显示模板的结果

	* @param string $tpl (template file)

	*/
	public function display($tpl){
		
		
		if($tpl==''){
		
			$tpl=strtolower(Request::instance()->controller()).'/'.strtolower(Request::instance()->action());
		}
		
		$tplfile=$this->parseTemplateFile($tpl);
		
		if (!file_exists($tplfile)) {
			exit('can not load template file : ' . $tplfile);
		}
		
		
		
		$compliefile = $this->compiledir . md5($tplfile) . '.php';

		
		
		if (!file_exists($compliefile) || filemtime($tplfile) > filemtime($compliefile)) {
			try{
			
			$this->parse($tplfile);
			}catch (\Exception $e){
				$e->getMessage();
			}
		}
		
		
		// 模板阵列变量分解成为独立变量
		extract($this->data, EXTR_OVERWRITE);
		
		// 页面缓存
		ob_start();
		ob_implicit_flush(0);
		include_once($compliefile);
		// 获取并清空缓存
		$content = ob_get_clean();
		// 内容过滤标签
		// 解析常量
		$content = $this->parseCL($content);
		
		
		echo   $content;
		
		
	   
	}
	public function returncontent($tpl){
	
	
		if($tpl==''){
	
			$tpl=strtolower(Request::instance()->controller()).'/'.strtolower(Request::instance()->action());
		}
	
		$tplfile=$this->parseTemplateFile($tpl);
	
		if (!file_exists($tplfile)) {
			exit('can not load template file : ' . $tplfile);
		}
	
	
	
		$compliefile = $this->compiledir . md5($tplfile) . '.php';
	
	
	
		if (!file_exists($compliefile) || filemtime($tplfile) > filemtime($compliefile)) {
			try{
	
				$this->parse($tplfile);
			}catch (\Exception $e){
				$e->getMessage();
			}
		}
	
	
		// 模板阵列变量分解成为独立变量
		extract($this->data, EXTR_OVERWRITE);
	
		// 页面缓存
		ob_start();
		ob_implicit_flush(0);
		include_once($compliefile);
		// 获取并清空缓存
		$content = ob_get_clean();
		// 内容过滤标签
		$content = $this->parseCL($content);
		return   $content;
	
	
	
	}
}